<?php

declare( strict_types = 1 );

namespace App\Common\Model\Base\DataUnit;

use App\Common as com;
use App\Common\Model\Base as nameBase;
use App\Common\Model\Base\DataUnit as nameDataUnit;

class DirectData extends nameDataUnit\Base {

	use com\Lib\Com\GoWeakReferencePool\Traits;

	/**
	 * @var array
	 */
	private array $_dataCaches = [];

	/**
	 * 是否在下次调用 $this->get() 方法时加锁（$this->get()执行后自动重置为false）
	 * @var bool
	 */
	private bool $isNextLock = false;

	/**
	 * 检查是否存在直属数据where，不存在则抛出异常
	 * @return bool [true][必反]
	 * @throws
	 */
	private function checkExistsWhereOrFail(  ):bool {
		if( $this->isExistsWhere() === false ){
			throw com\Lib\Err\Exception::makeOfNew( '缺少where条件', com\Lib\Err\Code::$sysDataExist );
		}
		//
		return true;
	}

	/**
	 * 是否存在直属数据where
	 * @return bool
	 */
	public function isExistsWhere(  ):bool {
		return ! empty( $this->getDataUnit()->goModelQuery()->getQuery()->wheres );
	}

	/**
	 * @return \Hyperf\Database\Model\Builder
	 */
	public function getModel(  ):\Hyperf\Database\Model\Builder {
		return $this->getDataUnit()->goModelQueryCloneOfNew();
		// return $this->getDataUnit()->goModelQuery();
	}

	/**
	 * 是否存在直属数据
	 * @return bool
	 */
	public function isExists(  ):bool {
		return $this->getModel()->value( 'id' ) !== null;
	}

	/**
	 * 获取直属数据(返回指定字段)
	 * @param string $field 返回字段
	 * @param mixed $default 默认数据
	 * @return mixed [{null}]数据不存在
	 * @throws
	 */
	public function get( string $field, mixed $default = null ):mixed {
		return $this->gets( [ $field ] )[ $field ] ?? $default;
	}

	/**
	 * 获取直属数据
	 * @param array|null $field 返回字段 [null]全部
	 * @return array|null [{null}]数据不存在 [{array}]数据组
	 * @throws
	 */
	public function gets( ?array $field = null ):?array {
		// $this->checkExistsWhereOrFail();
		//
		$model = $this->getModel();
		//
		if( $this->isNextLock ){
			$model->lock( true );
			$this->isNextLock = false; // 恢复
		}
		//
		if( $field === null ){
			$modelData = $model->first();
		}else{
			$modelData = $model->first( $this->getDataUnit()->getFieldAlias( $field ) );
		}
		//
		if( empty( $modelData ) ){
			return null;
		}
		//
		return $modelData->toArray();
	}

	/**
	 * 获取直属数据(记录不存在时返回null)
	 * @param array|string $field 返回字段。[{array}]数据组 [{string}]指定数据
	 * @param mixed $default 默认数据
	 * @return array|string [{null}]数据不存在 [{array}]数据组 [{string}]指定数据
	 * @throws
	 */
/*	public function getOrNull( $field = [], $default = null ) {
		try{
			return $this->get( $field, $default );
		}catch ( \Exception $e ){
			if( $e->getCode() === com\Lib\Err\Code::$sysSqlDataNotExist ){
				return null;
			}else{
				throw $e;
			}

		}
	}*/

	/**
	 * 获取直属数据(数据不存在时抛出异常)
	 * @param array|string|null $field 返回字段 [{array}]数据组 [{string}]指定数据 [null]返回全部
	 * @return mixed|array|string [{array}]数据组 [{string}]指定数据
	 * @throws
	 */
	public function getOrFail( array|string|null $field = null ):mixed {
		// $this->checkExistsWhereOrFail();
		//
		$model = $this->getModel();
		// 指定数据
		if( $field !== null ){
			$model->select( $field );
		}
		$modelData = $model->first();
		if( empty( $modelData ) ){
			$tableName = $model->getModel()->getTable();
			throw com\Lib\Err\Exception::makeOfNew( "数据不存在({$tableName})", com\Lib\Err\Code::$sysSqlDataNotExist );
		}
		$data = $modelData->toArray();
		if( is_string( $field ) ){
			return $data[ $field ];
		}else{
			return $data;
		}
	}

	/**
	 * 获取直属数据(缓存中读取，首次会加载全部数据)
	 * @param string $field 返回字段 [{string}]指定数据 [null]返回全部
	 * @param mixed $default 默认数据
	 * @param bool $isForce
	 * @return mixed|null [null]数据不存在
	 * @throws
	 */
	public function getOfCache( string $field, mixed $default = null, bool $isForce = false ) {
		return $this->getsOfCache( [ $field ], $isForce )[ $field ] ?? $default;
	}

	/**
	 * 获取直属数据(缓存中读取，首次会加载全部数据)
	 * @param array|null $field 返回字段  [{array}]数据组 [null]返回全部
	 * @param bool $isForce
	 * @return array|string|null [null]数据不存在 [obj]一行数据
	 * @throws
	 */
	public function getsOfCache( ?array $field = null, bool $isForce = false ) {
		// $this->checkExistsWhereOrFail();
		// 检查是否存在缓存
		$isNeedGet = false;
		if( ! $isForce ){
			if( is_array( $field ) ){
				foreach( $field as $fieldValue ){
					if( ! array_key_exists( $fieldValue, $this->_dataCaches ) ){
						$isNeedGet = true;
						break;
					}
				}
			}else{
				$isNeedGet = true;
			}
		}
		// 获取数据
		if ( $isNeedGet === true || $isForce === true ) {
			$datas = $this->gets( $field );
			if( $datas === null ){
				return null;
			}
			// 合并缓存
			$this->_dataCaches = array_merge( $this->_dataCaches, $datas );
		}
		// 返回数据
		if ( $field === null ) {
			// 全部返回
			return $this->_dataCaches;
		}else{
			if( empty( $field ) ){
				throw com\Lib\Err\Exception::makeOfNew( '不能为空', com\Lib\Err\Code::$sysFunParamError );
			}
			// 取出部分字段返回
			$matchData = com\Lib\Fun\Array_::assignField( $this->_dataCaches, $field );
			if( count( $field ) !== count( $matchData ) ) {
				throw com\Lib\Err\Exception::makeOfNew( 'field数量不匹配', com\Lib\Err\Code::$sysDataNotExist );
			}
			return $matchData;
		}
	}


	/**
	 * 设置直属数据
	 * @param string $field 字段
	 * @param mixed $value 数据
	 * @return $this
	 */
	public function setOfCache( string $field, mixed $value ):self {
		$this->_dataCaches[ $field ] = $value;

		return $this;
	}

	/**
	 * 设置直属数据
	 * @param array $datas
	 * @return $this
	 */
	public function setsOfCache( array $datas ):self {
		// 合并缓存
		$this->_dataCaches = array_merge( $this->_dataCaches, $datas );
		//
		return $this;
	}

	/**
	 * 获取已经缓存的数据
	 * @return array
	 */
	public function getCacheDatas(  ):array {
		return $this->_dataCaches;
	}

	/**
	 * 加锁并缓存数据
	 * @return $this
	 * @throws
	 */
	public function lockAndCaches(  ):self {
		$this->isNextLock = true;
		$this->getsOfCache();
		//
		return $this;
	}

	/**
	 * 检查DataCache是否存在指定字段
	 * @param array|string $field [array]指定多个
	 * @return bool 其中一个不存在则返回false
	 */
	private function isExistsOfDataCache( array|string $field ):bool {
		$fields = is_array( $field ) ? $field : [ $field ];
		foreach( $fields as $fieldValue ){
			if( ! isset( $this->_dataCaches[ $fieldValue ] ) ){
				return false;
			}
		}
		//
		return true;
	}

	/**
	 * 刷新缓存
	 * @return $this
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function refreshCache(  ):self {
		$this->getsOfCache( array_keys( $this->_dataCaches ), true );
		//
		return $this;
	}

	/**
	 * 清空缓存
	 * @return void
	 */
	public function clearCache(  ):void {
		$this->_dataCaches = [];
	}

}
